
package com.joysuch.wwyt.core.security;

import com.joysuch.wwyt.core.bean.UsernamePasswordTokenWithCompanyCode;
import com.joysuch.wwyt.core.common.exception.UnactivatedAccountException;
import com.joysuch.wwyt.core.common.security.CredentialsDigest;
import com.joysuch.wwyt.core.common.security.CredentialsMatcherAdapter;
import com.joysuch.wwyt.core.common.util.Encodes;
import com.joysuch.wwyt.core.constant.Constants;
import com.joysuch.wwyt.core.entity.BaseLoginAccount;
import com.joysuch.wwyt.core.service.AuthorityService;
import com.joysuch.wwyt.core.service.UserShiroService;
import javafx.util.Pair;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.util.CollectionUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Set;

/**
 * ShiroDbRealm
 */
public class ShiroDbRealm extends AuthorizingRealm implements InitializingBean {
    /**
     * 认证回调函数,登录时调用.
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
            throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        BaseLoginAccount user = null;
        if (token instanceof UsernamePasswordTokenWithCompanyCode) {
            user = userShiroService.findByUserNameAndCompanyCode(token.getUsername(),
                    ((UsernamePasswordTokenWithCompanyCode) token).getCompanyCode());
        } else {
            user = userShiroService.findByUserName(token.getUsername());

        }
        // 前后台登录共用，非管理员也可登录。
        if (user != null) {
            if (user.isNormal()) {
                // 判断是否超级管理员
                ShiroUser shiroUser = new ShiroUser(user.getId(), user.getBaseUser().getId(), user.getUserName());
                shiroUser.setSuperAdmin(Constants.SUPER_ADMIN_ROLE_NAME.equals(user.getRole()));
                byte[] salt = Encodes.decodeHex(user.getSalt());
                return new SimpleAuthenticationInfo(shiroUser, user.getPassword(), ByteSource.Util.bytes(salt),
                        getName());
            } else if (user.isLocked()) {
                throw new LockedAccountException();
            } else if (user.isUnactivated()) {
                throw new UnactivatedAccountException();
            }
        }
        return null;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo auth = new SimpleAuthorizationInfo();
        ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
        BaseLoginAccount user = userShiroService.get(shiroUser.id);
        if (user != null && user.isNormal()) {
            Pair<Set<String>, Set<String>> pair = authorityService.queryUserPerms(user.getBaseUser().getId());
            Set<String> perms = pair.getKey();
            Set<String> roles = pair.getValue();
            if (!CollectionUtils.isEmpty(roles)) {
                auth.setRoles(roles);
            }
            if (!CollectionUtils.isEmpty(perms)) {
                auth.setStringPermissions(perms);
            }
            // if (user.isSuper()) {
            // auth.addRole("super");
            // }
        }
        return auth;
    }

    @Override
    public boolean hasRole(PrincipalCollection principal, String roleIdentifier) {
        ShiroUser user = (ShiroUser) SecurityUtils.getSubject().getPrincipal();
        return user.isSuperAdmin() || super.hasRole(principal, roleIdentifier);
    }

    @Override
    public boolean isPermitted(PrincipalCollection principals, String permission) {
        ShiroUser user = (ShiroUser) SecurityUtils.getSubject().getPrincipal();
        return user.isSuperAdmin() || super.isPermitted(principals, permission);
    }

    /**
     * 设定Password校验的Hash算法与迭代次数.
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        CredentialsMatcher matcher = new CredentialsMatcherAdapter(credentialsDigest);
        setCredentialsMatcher(matcher);
    }

    protected UserShiroService userShiroService;
    private CredentialsDigest credentialsDigest;
    private AuthorityService authorityService;

    @Autowired
    public void setAuthorityService(AuthorityService authorityService) {
        this.authorityService = authorityService;
    }

    @Autowired
    public void setUserShiroService(UserShiroService userShiroService) {
        this.userShiroService = userShiroService;
    }

    @Autowired
    public void setCredentialsDigest(CredentialsDigest credentialsDigest) {
        this.credentialsDigest = credentialsDigest;
    }

}
